/***************************************************************************
 *   Copyright (C) 2006 by Archimedes Projekt   *
 *   thelsb@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/


#include <errno.h>

//#include <sys/types.h>


#include <QtDebug>
#include "qhkp.h"
//#include <sys/stat.h>
#include <linux/types.h>
//#include <sys/fcntl.h>
//#include <sys/ioctl.h>
//#include <asm/ioctl.h>
#include <linux/types.h>
typedef unsigned long kernel_ulong_t;
#include <asm-i386/types.h>
#define BITS_PER_LONG 32
#ifndef INC_hkp_drv
#define class _class 
#include "hkp_drv.h"
#undef class
#endif



QHkp::QHkp(QString hkpDev,QObject *parent)
 : QFile(parent),
 m_outAdata(0),
 m_outBdata(0)
{
    m_ownedPorts = new struct t_hkp_drv_own;
    memset(m_ownedPorts,0,sizeof(t_hkp_drv_own));
    
  m_dev = hkpDev;
  setFileName(hkpDev);
  if(!m_dev.isEmpty())
  {
   if(!open( QIODevice::ReadWrite|QIODevice::Unbuffered ) )
    qWarning( "could not open hacker port - device\"%s\": %s\n", hkpDev.toAscii(), strerror( errno ) );
   else {
         m_open = true; 
         m_hkp_fd = handle();
         initPorts();
         qDebug()<<"init fd:"<<m_hkp_fd;
        }
  }  
}


QHkp::~QHkp()
{
 if( isOpen()) close();
}

QString& QHkp::device()
{
    return m_dev;
}

void QHkp::setHkpDevice(const QString &dev)
{
   m_dev = dev;
}


QHkp& QHkp::operator=(const QHkp& instance)
{
    return (QHkp&)instance;
}


int QHkp::writeData(port p, unsigned char data)
{
  t_hkp_drv_act writeAct;
  quint8 lastData = 0;
  switch(p)
 {
  case A: 
      lastData = m_outAdata;
      m_outAdata = data;
      writeAct.type = hkp_drv_act_out_a;
   break;
  case B: 
      lastData = m_outBdata;
      m_outBdata = data;
      writeAct.type = hkp_drv_act_out_b;
    break;
     default: break;
 }
 //execute action
 writeAct.data.out.x = data;
 int err =  ioctl( m_hkp_fd, hkp_drv_ioctl_no_act, &writeAct );
 if( err != 0 )
  {
    printf( "  error: %s\n", strerror( errno ) );
    switch(p)
    {
        case A:
            m_outAdata = lastData;
            break;
        case B:
            m_outBdata = lastData;
            break;
        default: break;
    }
  }
 return err;
}


int QHkp::readData(port p,unsigned char *data)
{
  t_hkp_drv_act readAct;
  switch(p)
 {
  case A: readAct.type = hkp_drv_act_in_a;
   break;
  case B: readAct.type = hkp_drv_act_in_b;
   break;
  case analogA: readAct.type = hkp_drv_act_analog_a;
   break;
  case analogB: readAct.type = hkp_drv_act_analog_b;
   break;
  case analogC: readAct.type = hkp_drv_act_analog_c;
   break;
  case analogD: readAct.type = hkp_drv_act_analog_d;
   break;
  case analogE: readAct.type = hkp_drv_act_analog_e;
   break;
  case analogAll: readAct.type = hkp_drv_act_analog_all;
   break;     
 }
 //execute action
  //qDebug("fd:%d %d",m_hkp_fd,handle());
  int err = ioctl( m_hkp_fd, hkp_drv_ioctl_no_act, &readAct );
  if( err != 0 )
  {
    printf( "  error: %s\n", strerror( errno ) );
  }

  switch(p)
  {
   case A:
   case B:*data = readAct.data.in.x;
    break;
   case analogA: *data = readAct.data.analog_all.a;
    break;
   case analogB: *data = readAct.data.analog_all.b;
    break;
   case analogC: *data = readAct.data.analog_all.c;
    break;
   case analogD: *data = readAct.data.analog_all.d;
    break;
   case analogE: *data = readAct.data.analog_all.e;
    break;
   case analogAll: memcpy(data,&readAct.data.analog_all,sizeof(readAct.data.analog_all));
   break;
  }

  
  
  //emit dataReady(*data);
 return err;
}



int QHkp::ownInputPort(port p)
{
  //struct t_hkp_drv_own own;
//  char * p_param;//, flag;
//  unsigned int i;
  int err;

//   //initialize port ownership request structure
//   for( i = 0; i < arrElements( ((*m_ownedPorts)).out ); i++ )
//       (*m_ownedPorts).out[i] = 0;
//   for( i = 0; i < arrElements( (*m_ownedPorts).in ); i++ )
//       (*m_ownedPorts).in[i] = 0;
//   for( i = 0; i < arrElements( (*m_ownedPorts).pwm ); i++ )
//       (*m_ownedPorts).pwm[i] = 0;
//   for( i = 0; i < arrElements( (*m_ownedPorts).analog ); i++ )
//       (*m_ownedPorts).analog[i] = 0;
//   (*m_ownedPorts).ser = 0;
//   (*m_ownedPorts).i2c = 0;

 switch(p)
 {
     case A: (*m_ownedPorts).in[0] = 1;
   break;
   case B: (*m_ownedPorts).in[1] = 1;
   break;
   case analogA: (*m_ownedPorts).analog[0]=1;
   break;
   case analogB: (*m_ownedPorts).analog[1]=1;
   break;
   case analogC: (*m_ownedPorts).analog[2]=1;
   break;
   case analogD: (*m_ownedPorts).analog[3]=1;
   break;
   case analogE: (*m_ownedPorts).analog[4]=1;
   break;
   case analogAll: (*m_ownedPorts).analog[0]=1;
   (*m_ownedPorts).analog[1]=1;
   (*m_ownedPorts).analog[2]=1;
   (*m_ownedPorts).analog[3]=1;
   (*m_ownedPorts).analog[4]=1;
   break;  
     default: break;  
 }
 
  //request / release ownership
 err = ioctl( m_hkp_fd, hkp_drv_ioctl_no_own, m_ownedPorts );
  
  if( err != 0 )
  {
    printf( "  error: %s\n", strerror( errno ) );
  }
 return err;
}

int QHkp::ownOutputPort(port p)
{
//    struct t_hkp_drv_own own;
//  char * p_param;//, flag;
//  unsigned int i;
  int err;

  //initialize port ownership request structure
//   for( i = 0; i < arrElements( (*m_ownedPorts).out ); i++ )
//       (*m_ownedPorts).out[i] = 0;
//   for( i = 0; i < arrElements( (*m_ownedPorts).in ); i++ )
//       (*m_ownedPorts).in[i] = 0;
//   for( i = 0; i < arrElements( (*m_ownedPorts).pwm ); i++ )
//       (*m_ownedPorts).pwm[i] = 0;
//   for( i = 0; i < arrElements( (*m_ownedPorts).analog ); i++ )
//       (*m_ownedPorts).analog[i] = 0;
//   (*m_ownedPorts).ser = 0;
//   (*m_ownedPorts).i2c = 0;

 switch(p)
 {
     case A: (*m_ownedPorts).out[0] = 1;
       qDebug("set owning out a");
   break;
   case B: (*m_ownedPorts).out[1] = 1;
   qDebug("owning out b");
   break;
     default: break;
 }
  //request / release ownership
 err = ioctl( m_hkp_fd, hkp_drv_ioctl_no_own, m_ownedPorts );
  if( err != 0 )
  {
    printf( "  error: %s\n", strerror( errno ) );
  }
  return err;
}

/*!
    \fn QHkp::setOn(port outPort,pin p) 
 */
void QHkp::setOn(port p,int pin) 
{
    qDebug()<<"QHkp::setOn";
    //if(pin >=0 && pin < 129)
    //{
        switch(p)
        {
            case A:
                qDebug()<<"A:pin:"<<pin;
                if(isPortOwned(p,false))
                    writeData(p,m_outAdata |= pin);
                else qDebug()<<"port not owned";
                break;
            case B:
                qDebug()<<"B:pin:"<<pin;
                if(isPortOwned(p,false))
                    writeData(p,m_outBdata |= pin);
                else qDebug()<<"port not owned";
                break;            
            default: qDebug()<<"QHkp::setOn(): bad port.";
                break;     
        }
    //}
}


/*!
    \fn QHkp::releasePort(port p)
 */
void QHkp::releasePort(port p,bool inPort)
{
    //struct t_hkp_drv_own own;
//    unsigned int i;
    int err;
    
    switch(p)
    {
        case A: 
            if(inPort) (*m_ownedPorts).in[0] = 0;
            else  (*m_ownedPorts).out[0] = 0;
        break;
        case B:
            if(inPort) (*m_ownedPorts).in[1] = 0;
            else (*m_ownedPorts).out[1] = 0;
        break;
        default: break;
    }
  //request / release ownership
    err = ioctl( m_hkp_fd, hkp_drv_ioctl_no_own, m_ownedPorts );
    if( err != 0 )
    {
        printf( "  error: %s\n", strerror( errno ) );
    }
//    return err;
}


/*!
    \fn QHkp::isPortOwned(port p)
 */
bool QHkp::isPortOwned(port p,bool inPort)
{
    switch(p)
    {
        case A: 
            if(inPort)
                if((*m_ownedPorts).in[0]) return true;
            else if((*m_ownedPorts).out[0]) return true;    
        break;
        case B:
            if(inPort) 
                if((*m_ownedPorts).in[1]) return true;
            else if((*m_ownedPorts).out[1]) return true;
        break;
        case analogA: if((*m_ownedPorts).analog[0]) return true;
                      return false;
        break;
        case analogB: if((*m_ownedPorts).analog[1]) return true;
                      return false;
        break;
        case analogC: if((*m_ownedPorts).analog[2]) return true;
                      return false;
        break;
        case analogD: if((*m_ownedPorts).analog[3]) return true;
                      return false;
        break;
        case analogE: if((*m_ownedPorts).analog[4]) return true;
                      return false;
        break;
        case analogAll: if((*m_ownedPorts).analog[0] && (*m_ownedPorts).analog[1]
                        && (*m_ownedPorts).analog[2] && (*m_ownedPorts).analog[3]
                        && (*m_ownedPorts).analog[4]) return true;
                        return false;
        break;  
        default: return false;  
    }
}


/*!
    \fn QHkp::setOff(port port, int pin)
 */
void QHkp::setOff(port p, int pinMask)
{
    qDebug()<<"QHkp::setOff";
    //if(pinMask >=0 && pinMask < 129)
    //{
        switch(p)
        {
            case A:
                
                if(isPortOwned(p,false))
                    writeData(p,m_outAdata &= ~pinMask);
                else qDebug()<<"port not owned";
                qDebug()<<"setOff: A:pin:"<<m_outAdata<<" "<<~pinMask;
                break;
            case B:
                
                if(isPortOwned(p,false))
                    writeData(p,m_outBdata &= ~pinMask);
                else qDebug()<<"port not owned";
                qDebug()<<"setOff: B:pin:"<<m_outBdata;
                break;            
           default:  qDebug()<<"QHkp::setOff(): bad port.";
                    break;     
        }
    //}
}


/*!
    \fn QHkp::getPin(port p,int pin)
 */
bool QHkp::pin(port p,bool inPort,int pinMask)
{
    
    if(isPortOwned(p,inPort))
    {    
        quint8 data=0;
        if(inPort)
        readData(p,&data);
        else switch(p)
        {
            case A: std::cout<<"A out:"<<(float)m_outAdata<<std::endl;
                return m_outAdata & pinMask;
                break;
            case B: std::cout<<"B out:"<<(float)m_outBdata<<std::endl;
                return m_outBdata & pinMask;
                break;
            default: return false; 
                
        }
        return data & pinMask;
    }
    
    
   
    return false;
}


/*!
    \fn QHkp::initPorts()
 */
void QHkp::initPorts()
{
    
     //initialize port ownership request structure
    for( int i = 0; i < (int)arrElements( ((*m_ownedPorts)).out ); i++ )
        (*m_ownedPorts).out[i] = 0;
    for( int i = 0; i < (int)arrElements( (*m_ownedPorts).in ); i++ )
        (*m_ownedPorts).in[i] = 0;
    for( int i = 0; i < (int)arrElements( (*m_ownedPorts).pwm ); i++ )
        (*m_ownedPorts).pwm[i] = 0;
    for( int i = 0; i < (int)arrElements( (*m_ownedPorts).analog ); i++ )
        (*m_ownedPorts).analog[i] = 0;
    (*m_ownedPorts).ser = 0;
    (*m_ownedPorts).i2c = 0;
}


/*!
    \fn QHkp::ownPort(port p,bool inPort)
 */
void QHkp::ownPort(port p,bool inPort)
{
    if(inPort) ownInputPort(p);
    else ownOutputPort(p);
}


/*!
    \fn QHkp::getPins(port p)
 */
quint8 QHkp::getPins(port p)
{
    switch(p)
    {
        case A: return m_outAdata;
            break;
        case B: return m_outBdata;
            break;
        default:
            break;    
    }
}


/*!
    \fn QHkp::clearPort(port p)
 */
void QHkp::clearOutPort(port p)
{
    if(isPortOwned(p,false)) writeData(p,0);
    else std::cout<<"QHkp::clearOutPort(): port not owned"<<std::endl;
}
